/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Bademus
*/
package org.github.bademux.feedly.api;
import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
import com.google.api.client.auth.oauth2.AuthorizationCodeResponseUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import org.github.bademux.feedly.api.model.Category;
import org.github.bademux.feedly.api.model.Subscription;
import org.github.bademux.feedly.api.model.Tag;
import org.github.bademux.feedly.api.oauth2.FeedlyAuthorizationCodeFlow;
import org.github.bademux.feedly.api.oauth2.FeedlyCredential;
import org.github.bademux.feedly.api.oauth2.FeedlyTokenResponse;
import org.github.bademux.feedly.api.service.DevFeedly;
import org.github.bademux.feedly.api.service.DevFeedlyAuthorizationCodeFlow;
import org.github.bademux.feedly.api.service.Feedly;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkArgument;
import static com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull;
import static org.github.bademux.feedly.api.oauth2.FeedlyOAuthConstants.REDIRECT_URI_LOCAL;
public abstract class AbstractIntegrationTest {
public void setUp() throws IOException {
FeedlyCredential credential = login();
//setup Feedly service
service = new DevFeedly.Builder(httpTransport, jsonFactory, credential).build();
}
public void cleanUp() throws IOException {
//cleanUp
for (Subscription subscription : service.subscriptions().list().execute()) {
service.subscriptions().detete(subscription).execute();
}
for (Category category : service.categories().list().execute()) {
service.categories().detete(category).execute();
}
List<Tag> tags = service.tags().list().execute();
if(!tags.isEmpty()) {
service.tags().deteteByTag(tags).execute();
}
}
public static Subscription newSubscriptionWithCategory(Feedly service, String feedUrl) throws IOException {
Subscription subscription = new Subscription(feedUrl, "Test");
subscription.addCategory(service.newCategory("Test1"));
subscription.addCategory(service.newCategory("Test2"));
service.subscriptions().update(subscription).execute();
return subscription;
}
public FeedlyCredential login() throws IOException {
Properties test_prop = load("test_credentials.properties");
TEST_USER = checkNotNull(test_prop.getProperty("wordpress.user"));
TEST_PASSWORD = checkNotNull(test_prop.getProperty("wordpress.password"));
//login
Properties prop = load("user_secrets.properties");
String clientId = checkNotNull(prop.getProperty("feedly.client_id"));
String clientSecret = checkNotNull(prop.getProperty("feedly.client_secret"));
LOG.info("Using client_id:" + clientId + ", client_secret:" + clientSecret);
FeedlyAuthorizationCodeFlow flow = new DevFeedlyAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, clientId, clientSecret)
.setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR)).build();
FeedlyCredential credential = flow.loadCredential(TEST_USER);
if (credential == null) {
credential = checkNotNull(newFeedlyCredential(flow));
}
LOG.info("Use acess_token" + credential.getAccessToken() +
"UserId: " + credential.getUserId() + " Plan:" + credential.getPlan());
//setup Feedly service
return credential;
}
private static FeedlyCredential newFeedlyCredential(FeedlyAuthorizationCodeFlow flow)
throws IOException {
LOG.info("Fetch new access_token");
AuthorizationCodeRequestUrl oauthUrlObject = flow.newAuthorizationUrl()
.setRedirectUri(REDIRECT_URI_LOCAL).setState("com.feedly.developer.test-state");
String oauthUrl = oauthUrlObject.build();
LOG.info("OAuth2 Url: " + oauthUrl);
String responseUrlStr = authWithWordpress(TEST_USER, TEST_PASSWORD, oauthUrl);
// direct the end-user's browser to an authorization page to grant access to their protected data.
AuthorizationCodeResponseUrl responseUrl = new AuthorizationCodeResponseUrl(responseUrlStr);
if (responseUrl.getError() != null || responseUrl.getCode() == null) {
LOG.severe(responseUrl.getErrorDescription());
return null;
}
//check state
checkArgument(oauthUrlObject.getState().equals(responseUrl.getState()));
FeedlyTokenResponse token = flow.newTokenRequest(responseUrl.getCode())
.setRedirectUri(oauthUrlObject.getRedirectUri()).execute();
return flow.createAndStoreCredential(checkNotNull(token), TEST_USER);
}
public static Properties load(String fileName) throws IOException {
Properties prop = new Properties();
prop.load(new FileReader(AbstractIntegrationTest.class.getResource('/' + fileName).getFile()));
return prop;
}
public static String authWithWordpress(String usr, String pwd, String oauthUrl)
throws IOException {
//1. This will load the home page and get Auth link
String authUrl = Jsoup.connect(oauthUrl).timeout(5000).get()
.getElementsByAttributeValueContaining("href", "wordpress.com").attr("href");
//2. Auth
Element loginform = Jsoup.connect(authUrl).referrer(oauthUrl).get().getElementById("loginform");
if (loginform == null) {
throw new IllegalStateException("Can't parse loginform");
}
//2.1 Login
Connection.Response response = Jsoup.connect(loginform.attr("action"))
.data("log", usr)
.data("pwd", pwd)
.data("wp-submit", loginform.select("input[name=wp-submit]").attr("value"))
.data("action", loginform.select("input[name=action]").attr("value"))
.data("redirect_to", loginform.select("input[name=redirect_to]").attr("value"))
.method(Connection.Method.POST).timeout(5000).execute();
// 2.2 Approve
Element loginformApprove = response.parse().getElementById("loginform");
if (loginformApprove == null) {
throw new IllegalStateException("Can't parse loginform");
}
String redirectUrl = Jsoup.connect(loginformApprove.attr("action")).cookies(response.cookies())
.data("client_id", loginformApprove.select("input[name=client_id]").attr("value"))
.data("response_type", loginformApprove.select("input[name=response_type]").attr("value"))
.data("redirect_uri", loginformApprove.select("input[name=redirect_uri]").attr("value"))
.data("state", loginformApprove.select("input[name=state]").attr("value"))
.data("action", loginformApprove.select("input[name=action]").attr("value"))
.data("blog_id", loginformApprove.select("input[name=blog_id]").attr("value"))
.data("_wpnonce", loginformApprove.select("input[name=_wpnonce]").attr("value"))
.data("wp-submit", loginformApprove.select("input[name=wp-submit]").attr("value"))
.data("redirect_to", loginformApprove.select("input[name=redirect_to]").attr("value"))
.method(Connection.Method.GET).followRedirects(false)
.timeout(5000).execute().header("Location");
// 3 link with code
return Jsoup.connect(redirectUrl).ignoreContentType(true).followRedirects(false)
.timeout(5000).method(Connection.Method.GET).execute().header("Location");
}
protected <T extends GenericJson> T findIn(Collection<T> list, String id) {
if (!list.isEmpty() && id != null) {
for (T item : list) {
if (((String) item.get("id")).endsWith(id)) {
return item;
}
}
}
return null;
}
protected Feedly service;
protected static String TEST_USER;
protected static String TEST_PASSWORD;
//init logger and markAs conf
private final static Logger LOG = Logger.getLogger(AbstractIntegrationTest.class.getSimpleName());
static {
try {
LogManager.getLogManager()
.readConfiguration(AbstractIntegrationTest.class.getResourceAsStream("/logging.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static final File DATA_STORE_DIR =
new File(AbstractIntegrationTest.class.getResource("/").getPath(), "tmp");
// new java.io.File(System.getProperty("user.home"), ".store/feedly-api");
static final HttpTransport httpTransport = new NetHttpTransport();
static final JsonFactory jsonFactory = new GsonFactory();
}